import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics.pairwise import cosine_similarity
from matplotlib.colors import LinearSegmentedColormap
import networkx as nx

# 设置中文字体
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 电影内容数据（含类型、导演、演员等特征）
movies = pd.DataFrame({
    'movie_id': [1, 2, 3, 4, 5],
    'title': ['银翼杀手2049', '沙丘', '星际穿越', '泰坦尼克号', '罗马假日'],
    'genres': ['科幻/悬疑', '科幻/冒险', '科幻/剧情', '爱情/灾难', '爱情/喜剧'],
    'director': ['丹尼斯·维伦纽瓦', '丹尼斯·维伦纽瓦', '克里斯托弗·诺兰', '詹姆斯·卡梅隆', '威廉·惠勒'],
    'actors': ['瑞恩·高斯林', '提莫西·查拉梅', '马修·麦康纳', '莱昂纳多·迪卡普里奥', '格利高里·派克']
})

# 用户评分数据（模拟5个用户对部分电影的评分，1-5分）
ratings = pd.DataFrame({
    'user_id': [1, 1, 2, 2, 3, 3, 4, 5, 5],
    'movie_id': [1, 3, 2, 4, 1, 5, 3, 2, 4],
    'rating': [4, 5, 3, 5, 2, 4, 5, 4, 3]
})

# 合并电影与评分数据，生成用户-电影评分矩阵
user_movie_matrix = ratings.pivot_table(index='user_id', columns='movie_id', values='rating', fill_value=0)

# 提取电影内容特征（拼接类型、导演、演员为文本）
movies['features'] = movies['genres'] + ' ' + movies['director'] + ' ' + movies['actors']
vectorizer = TfidfVectorizer(stop_words='english')
movie_features = vectorizer.fit_transform(movies['features'])

# 1. 基于内容的推荐：计算电影相似度
movie_similarity = cosine_similarity(movie_features)

# 2. 基于用户的协同过滤：计算用户相似度
user_similarity = cosine_similarity(user_movie_matrix)


# 3. 混合推荐：为用户1生成推荐（假设用户ID=1）
def hybrid_recommendation(user_id, top_n=3):
    # 获取用户评分记录
    user_ratings = user_movie_matrix.loc[user_id]
    liked_movies = user_ratings[user_ratings > 3].index  # 筛选评分>3的电影

    # 基于内容推荐：扩展相似电影
    content_recommendations = []
    for movie in liked_movies:
        similar_indices = np.argsort(movie_similarity[movie - 1])[::-1][1:4]  # 取前3个相似电影（排除自身）
        content_recommendations.extend(movies.iloc[similar_indices]['movie_id'])

    # 基于用户协同过滤：找相似用户的高评分电影
    user_neighbors = np.argsort(user_similarity[user_id - 1])[::-1][1:3]  # 取前2个相似用户
    cf_recommendations = []
    for neighbor in user_neighbors:
        neighbor_ratings = user_movie_matrix.iloc[neighbor]
        high_rated = neighbor_ratings[neighbor_ratings > 4].index
        cf_recommendations.extend(high_rated)

    # 混合推荐结果（去重并按出现次数排序）
    combined = content_recommendations + cf_recommendations
    recommendation_counts = pd.Series(combined).value_counts()
    top_recommendations = recommendation_counts[~recommendation_counts.index.isin(liked_movies)].index[:top_n]

    return top_recommendations


# 生成推荐
user_id = 1
recommended_movie_ids = hybrid_recommendation(user_id=user_id)
recommended_movies = movies[movies['movie_id'].isin(recommended_movie_ids)]


# 可视化1：推荐电影海报墙（文本替代）
def visualize_movie_posters():
    plt.figure(figsize=(12, 4))
    n_movies = len(recommended_movies)

    for i, (_, movie) in enumerate(recommended_movies.iterrows()):
        plt.subplot(1, n_movies, i + 1)
        # 用电影标题和类型创建"海报"
        plt.text(0.5, 0.7, movie['title'], fontsize=10, ha='center', va='center')
        plt.text(0.5, 0.4, f"类型: {movie['genres']}", fontsize=8, ha='center', va='center')
        plt.text(0.5, 0.2, f"导演: {movie['director']}", fontsize=8, ha='center', va='center')
        plt.axis('off')
        plt.title(f"推荐 #{i + 1}", fontsize=10)

    plt.suptitle(f"为用户 {user_id} 推荐的电影", fontsize=14, y=1.02)
    plt.tight_layout()
    plt.show()


# 可视化2：推荐来源分析（内容 vs 协同过滤）
def visualize_recommendation_sources():
    # 获取用户评分记录
    user_ratings = user_movie_matrix.loc[user_id]
    liked_movies = user_ratings[user_ratings > 3].index

    # 基于内容的推荐
    content_recommendations = []
    content_sources = {}
    for movie in liked_movies:
        similar_indices = np.argsort(movie_similarity[movie - 1])[::-1][1:4]
        similar_movies = movies.iloc[similar_indices]['movie_id']
        content_recommendations.extend(similar_movies)
        for sim_movie in similar_movies:
            content_sources[sim_movie] = movie

    # 基于用户协同过滤的推荐
    user_neighbors = np.argsort(user_similarity[user_id - 1])[::-1][1:3]
    cf_recommendations = []
    cf_sources = {}
    for neighbor in user_neighbors:
        neighbor_ratings = user_movie_matrix.iloc[neighbor]
        high_rated = neighbor_ratings[neighbor_ratings > 4].index
        cf_recommendations.extend(high_rated)
        for hr_movie in high_rated:
            cf_sources[hr_movie] = neighbor

    # 统计推荐来源
    rec_sources = {'Content': 0, 'Collaborative': 0, 'Both': 0}
    for rec in recommended_movie_ids:
        from_content = rec in content_recommendations
        from_cf = rec in cf_recommendations
        if from_content and from_cf:
            rec_sources['Both'] += 1
        elif from_content:
            rec_sources['Content'] += 1
        else:
            rec_sources['Collaborative'] += 1

    # 绘制饼图
    plt.figure(figsize=(8, 6))
    plt.pie(rec_sources.values(), labels=rec_sources.keys(), autopct='%1.1f%%',
            colors=['#66b3ff', '#99ff99', '#ffcc99'], startangle=90)
    plt.title(f"推荐来源分析 (用户 {user_id})", fontsize=14)
    plt.axis('equal')
    plt.show()


# 可视化3：用户偏好与推荐电影的特征相似度热图
def visualize_feature_similarity():
    # 获取用户喜欢的电影
    user_ratings = user_movie_matrix.loc[user_id]
    liked_movies = user_ratings[user_ratings > 3].index
    liked_movie_features = movie_features[liked_movies - 1]

    # 获取推荐电影的特征
    recommended_features = movie_features[recommended_movie_ids - 1]

    # 计算相似度矩阵
    similarity_matrix = cosine_similarity(liked_movie_features, recommended_features)

    # 准备标签
    liked_movie_titles = [movies.iloc[idx - 1]['title'] for idx in liked_movies]
    recommended_titles = [movies.iloc[idx - 1]['title'] for idx in recommended_movie_ids]

    # 创建自定义颜色映射
    cmap = LinearSegmentedColormap.from_list('custom_cmap', ['#f0f9e8', '#bae4bc', '#7bccc4', '#2b8cbe'])

    # 绘制热图
    plt.figure(figsize=(10, 8))
    sns.heatmap(similarity_matrix, annot=True, cmap=cmap,
                xticklabels=recommended_titles, yticklabels=liked_movie_titles,
                linewidths=0.5, vmin=0, vmax=1)
    plt.title(f"用户 {user_id} 偏好与推荐电影的特征相似度", fontsize=14)
    plt.xlabel("推荐电影", fontsize=12)
    plt.ylabel("用户喜欢的电影", fontsize=12)
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()
    plt.show()


# 可视化4：推荐网络关系图
def visualize_recommendation_network():
    # 获取用户评分记录
    user_ratings = user_movie_matrix.loc[user_id]
    liked_movies = user_ratings[user_ratings > 3].index

    # 基于内容的推荐
    content_recommendations = {}
    for movie in liked_movies:
        similar_indices = np.argsort(movie_similarity[movie - 1])[::-1][1:4]
        similar_movies = movies.iloc[similar_indices]['movie_id']
        for sim_movie in similar_movies:
            content_recommendations[sim_movie] = movie

    # 基于用户协同过滤的推荐
    user_neighbors = np.argsort(user_similarity[user_id - 1])[::-1][1:3]
    cf_recommendations = {}
    for neighbor in user_neighbors:
        neighbor_ratings = user_movie_matrix.iloc[neighbor]
        high_rated = neighbor_ratings[neighbor_ratings > 4].index
        for hr_movie in high_rated:
            cf_recommendations[hr_movie] = neighbor

    # 创建网络图
    G = nx.Graph()

    # 添加节点
    G.add_node(f"用户 {user_id}", type="user", size=1000)

    # 添加用户喜欢的电影节点
    for movie_id in liked_movies:
        title = movies.iloc[movie_id - 1]['title']
        G.add_node(f"电影: {title}", type="liked_movie", size=500)
        G.add_edge(f"用户 {user_id}", f"电影: {title}", weight=1, color='blue')

    # 添加相似用户节点
    for neighbor in user_neighbors:
        G.add_node(f"相似用户 {neighbor + 1}", type="similar_user", size=700)
        G.add_edge(f"用户 {user_id}", f"相似用户 {neighbor + 1}", weight=user_similarity[user_id - 1][neighbor],
                   color='green')

    # 添加推荐电影节点和连接
    for rec_id in recommended_movie_ids:
        title = movies.iloc[rec_id - 1]['title']
        G.add_node(f"推荐: {title}", type="recommended", size=600)

        # 基于内容的连接
        if rec_id in content_recommendations:
            source_movie = content_recommendations[rec_id]
            source_title = movies.iloc[source_movie - 1]['title']
            G.add_edge(f"电影: {source_title}", f"推荐: {title}", weight=0.8, color='red')

        # 基于协同过滤的连接
        if rec_id in cf_recommendations:
            neighbor = cf_recommendations[rec_id]
            G.add_edge(f"相似用户 {neighbor + 1}", f"推荐: {title}", weight=0.7, color='purple')

    # 设置节点位置
    pos = nx.spring_layout(G, seed=42)

    # 绘制网络图
    plt.figure(figsize=(12, 10))

    # 按类型绘制节点
    node_colors = []
    node_sizes = []
    for node in G.nodes():
        if "用户" in node and "相似" not in node:
            node_colors.append('#FFA500')  # 橙色 - 目标用户
            node_sizes.append(1000)
        elif "相似用户" in node:
            node_colors.append('#00BFFF')  # 亮蓝色 - 相似用户
            node_sizes.append(700)
        elif "电影:" in node:
            node_colors.append('#90EE90')  # 浅绿色 - 用户喜欢的电影
            node_sizes.append(500)
        else:  # 推荐电影
            node_colors.append('#FF6347')  # 珊瑚色 - 推荐电影
            node_sizes.append(600)

    # 绘制节点
    nx.draw_networkx_nodes(G, pos, node_color=node_colors, node_size=node_sizes, alpha=0.8)

    # 按类型绘制边
    edge_colors = []
    edge_widths = []
    for _, _, attr in G.edges(data=True):
        edge_colors.append(attr['color'])
        edge_widths.append(attr['weight'] * 2)

    nx.draw_networkx_edges(G, pos, edge_color=edge_colors, width=edge_widths, alpha=0.6)

    # 绘制标签
    nx.draw_networkx_labels(G, pos, font_size=9, font_family='SimHei')

    plt.title(f"用户 {user_id} 的推荐网络关系图", fontsize=16)
    plt.axis('off')
    plt.tight_layout()
    plt.show()


# 执行可视化
visualize_movie_posters()
visualize_recommendation_sources()
visualize_feature_similarity()
visualize_recommendation_network()
